Technical Q&A

QTMTB 57 - How do I decompress individual frames into an offscreen? (24-April-2000)


Q: How can you scale and decompress individual frames of a video track into an offscreen RGB buffer so the pixels can be further manipulated?

A: The code snippets below show how a single frame can be drawn into a GWorld. Once the GWorld is created, use SetMovieGWorld() to specify the GWorld you would like the movie to draw into. Use GetMovieNextInterestingTime() to find the places where the movie will display a new video sample (frame), then set the movie position using SetMovieTimeValue(). The time value passed to SetMovieTimeValue() is returned by GetMovieNextInterestingTime(). Finally, task the movie using MovieTask(), which performs the actual drawing into your GWorld. You will end up with the GWorld containing the frame pixels ready for manipulation.

GWorlds are very flexible and can contain pixel formats other than RGB. For more information regarding GWorlds and drawing, refer to the following technical documentation:

The full sample application called VideoFrameToGWorld can be found on the QuickTime Sample Code page:

// Globals ---------------------------------------------------------------------
	GWorldPtr	gSrcGWorld = NULL;
	Movie		gMovie = NULL;
	TimeValue 	gMovieTime = 0; // set current time value to beginning of the movie
	UInt32	gFrameCount = -1;
	UInt32	gFrameNumber = 0;

// CountThemFrames -------------------------------------------------------------
// Count the number of video "frames" in the movie by stepping through
// all of the video "interesting times", or in other words, the places where the
// movie displays a new video sample. The time between these interesting times is not
// necessarily constant.
//------------------------------------------------------------------------------
void CountThemFrames( void )
{
	OSType	whichMediaType = VIDEO_TYPE;
	short		flags = nextTimeMediaSample + nextTimeEdgeOK;
	TimeValue	duration;
	TimeValue	theTime = 0;
	
	while (theTime >= 0) {
		gFrameCount++;
		GetMovieNextInterestingTime(gMovie,
						    flags,
						    1,
						    &whichMediaType,
						    theTime,
						    0,
						    &theTime,
						    &duration);

		//  after the first interesting time, don't include the time we
		//  are currently at.

		flags = nextTimeMediaSample;
	} // while
}

// MakeGWorld ------------------------------------------------------------------
//  Get the bounding rectangle of the movie and create a 32-bit gworld
//  with those dimensions.
//  This GWorld will be used for rendering movie frames into.
//------------------------------------------------------------------------------
void MakeGWorld( void )
{
	Rect srcRect;
	Rect portRect;
	
	OSErr err = noErr;
	
	// get the movies dimensions
	GetMovieBox(gMovie,&srcRect);
	
	err = NewGWorld(&gSrcGWorld,
			    k32ARGBPixelFormat,
			    &srcRect,
			    NULL,
			    NULL,
			    0);
	BailError(err);
	
	// make sure to lock the PixMap
	LockPixels(GetGWorldPixMap(gSrcGWorld));

	// erase
	SetGWorld(gSrcGWorld,NULL);
	GetPortBounds(gSrcGWorld, &portRect);
	EraseRect(&portRect);
	
	// *** tell the movie to draw into the GWorld ***
	SetMovieGWorld(gMovie,gSrcGWorld,GetGWorldDevice(gSrcGWorld));	

bail:
	return;
}


// NextFrame -------------------------------------------------------------------
//  Get the next frame of the movie, set the movie time for that frame,
//  then task the movie which will draw the frame to the GWorld.
//  Modify the movie matrix so the next frame will be rotated just for fun, and
//  finally, draw the frame number on top of the image and inval the window rect.
//------------------------------------------------------------------------------
void NextFrame( void )
{	
	if ( gFrameNumber < gFrameCount ) {

		TimeValue duration;
		
		// get the next frame of the source movie
		short 	flags = nextTimeMediaSample;
		OSType	whichMediaType = VIDEO_TYPE;

		// if this is the first frame, include the frame we are currently on
		if (gFrameNumber == 0)
			flags |= nextTimeEdgeOK;

		// skip to the next interesting time and get the duration for that frame
		GetMovieNextInterestingTime(gMovie,
						    flags,
						    1,
						    &whichMediaType,
						    gMovieTime,
						    0,
						    &gMovieTime,
						    &duration);

		// set the time for the frame and give time to the movie toolbox
		SetMovieTimeValue(gMovie,gMovieTime);

		// *** tasking the movie does the actual draw ***
		MoviesTask(gMovie,0);
	}

//------------------------------------------------------------------------------
// You now have pixels you can play with in the GWorld!
//------------------------------------------------------------------------------

}

-- Edward Agabeg
Worldwide Developer Technical Support

Technical Q&A | Contents
Previous Question

To contact us, please use the Contact Us page.